#include "ULA.h"
#include "Flags.h"
#include <iostream>

using namespace std;

ULA::ULA()
{
}

ULA::~ULA()
{
}


void ULA::ExecutaOperacao(int operando, u16bits ra, u16bits rb, u16bits &c, Flags *flags)
{
	switch(operando)
	{
		case 000: c =Zeros(flags); 				break;
		case 001: c =Ones(flags);				break;
		case 002: c =And(ra,rb,flags); 			break;
		case 003: c =Nand(ra,rb,flags); 		break;
		case 004: c =Or(ra,rb,flags);			break;
		case 005: c =Nor(ra,rb,flags); 		    break;
		case 006: c =Xor(ra,rb,flags);			break;
		case 007: c =Xnor(ra,rb,flags); 		break;
		case 011: c =Passa(ra,flags); 		    break;
		case 010: c =PassNota(ra,flags); 		break;
		case 012: c =AndNota(ra,rb,flags); 		break;
		case 013: c =OrNotb(ra,rb,flags); 		break;
		case 020: c =Lsl(ra,flags);				break;
		case 021: c =Asl(ra,flags);				break;
		case 022: c =Lsr(ra,flags);				break;
		case 023: c =Asr(ra,flags);				break;
		case 030: c =Add(ra,rb,flags);			break;
		case 031: c =Sub(ra,rb,flags);			break;
		case 032: c =AddInc(ra,rb,flags);		break;
		case 033: c =SubDec(ra,rb,flags);		break;
		case 034: c =IncA(ra,flags);			break;
		case 035: c =DecA(ra,flags);			break;		
		default: cout << "Operando Nao Existente";
		
	}
}


unsigned short ULA::Zeros(Flags *flags)
{
	//cout << "Entrei em One" <<endl;
	flags->setFlag(neg,false);
	flags->setFlag(zero,true);
	flags->setFlag(negzero,true);
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	flags->setFlag(vero,false);
	return 0;
}

unsigned short ULA::Ones(Flags *flags)
{
	//cout << "Entrei em One" <<endl;
//	flags->setFlag(neg,false);
//	flags->setFlag(zero,false);
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	return 1;
}

unsigned short ULA::And(unsigned short ra, unsigned short rb,Flags *flags)
{
	unsigned short c = ra & rb;

	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
		
	return c;
}

unsigned short ULA::Nand(unsigned short ra, unsigned short rb, Flags *flags)
{
	unsigned short c = ~(ra&rb);
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	return c;
}

unsigned short ULA::Or(unsigned short ra, unsigned short rb, Flags *flags)
{
	unsigned short c = (ra | rb);

	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);

	return c;
}

unsigned short ULA::Xor(unsigned short ra, unsigned short rb, Flags *flags)
{
	unsigned short c = (ra^rb);

	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	
	return c;
}

unsigned short ULA::Nor(unsigned short ra, unsigned short rb,Flags *flags)
{
	unsigned short c = ~(ra|rb);
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	return c;
}

unsigned short ULA::Xnor(unsigned short ra, unsigned short rb, Flags *flags)
{
	unsigned short c = ~(ra^rb);
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	return c;	
}

unsigned short ULA::Passa(unsigned short c,Flags *flags)
{

	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	
	return c;	
}

unsigned short ULA::PassNota(unsigned short c,Flags *flags)
{
	c = ~c;
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	return c;		
}

unsigned short ULA::AndNota(unsigned short ra, unsigned short rb,Flags *flags)
{
	unsigned short c= ((~ra)&rb);

	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	
	return c;		
}

unsigned short ULA::OrNotb(unsigned short ra, unsigned short rb,Flags *flags)
{
	unsigned short c= (ra|(~rb));
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	return c;
}

unsigned short ULA::Lsl(unsigned short ra,Flags *flags)
{
	bool b;
	unsigned short r = ra & 0x8000;
	if (r == 0x8000)
		b = true;
	else 
		b = false;
	flags->setFlag(carry,b);
	flags->setFlag(overflow,false);
	ra = ra << 1;
	if(ra & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);
	
	if(ra!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
		
	return ra;
}

unsigned short ULA::Asl(unsigned short ra,Flags *flags)
{
	bool b1, b2;
	unsigned short r = ra & 0x8000;
	if (r == 0x8000)
		b1 = true;
	else 
		b1 = false;
	
	r = ra & 0x4000;
	if (r == 0x4000)
		b2 = true;
	else 
		b2 = false;
	
	flags->setFlag(carry,b1);
	flags->setFlag(overflow, b1 xor b2);
	ra = ra << 1;
	if(ra & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);
	
	if(ra!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return ra;
}

unsigned short ULA::Lsr(unsigned short ra,Flags *flags)
{

	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	
	ra = ra >> 1;
	
	if(ra & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);
	
	if(ra!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return ra;
}

unsigned short ULA::Asr(unsigned short ra,Flags *flags)
{
	flags->setFlag(carry,false);
	flags->setFlag(overflow,false);
	
	unsigned short msb_ra = (ra & 0x8000);
	ra = (ra >> 1) | msb_ra;
	
	if(ra & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);
	
	if(ra!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return ra;
}

unsigned short ULA::Add(unsigned short ra, unsigned short rb,Flags *flags)
{
	
	unsigned int temp = ra+rb;		
	
	if(temp&0xF0000) flags->setFlag(carry,true);
	else flags->setFlag(carry,false);
	
	unsigned short c = (unsigned short) temp;  // Trunca
	
	//verifica Overflow
	flags->setFlag(overflow,false);
	if( (ra & 0x8000) == (rb & 0x8000) ) if( (c & 0x8000) != (ra & 0x8000) ) flags->setFlag(overflow, true);

	
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return c;
}

unsigned short ULA::AddInc(unsigned short ra, unsigned short rb,Flags *flags)
{
	unsigned int temp = ra+rb+1;		
	
	if(temp&0xF0000) flags->setFlag(carry,true);
	else flags->setFlag(carry,false);
	
	unsigned short c = (unsigned short) temp;  // Trunca
	
	//verifica Overflow
	flags->setFlag(overflow,false);
	if( (ra & 0x8000) == (rb & 0x8000) ) if( (c & 0x8000) != (ra & 0x8000) ) flags->setFlag(overflow, true);

	
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return c;
}

unsigned short ULA::DecA(unsigned short ra,Flags *flags)
{		
//	unsigned int temp = ra-1;	
	unsigned int temp = ra+0xFFFF;
	
	if(temp&0xF0000) flags->setFlag(carry,true);
	else flags->setFlag(carry,false);
	
	unsigned short c = (unsigned short) temp;  // Trunca
	
	//verifica Overflow
	flags->setFlag(overflow,false);
	if( (ra & 0x8000) == 0x8000) if( (c & 0x8000) != (ra & 0x8000) ) flags->setFlag(overflow, true);

	
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return c;
}

unsigned short ULA::IncA(unsigned short ra,Flags *flags)
{
	unsigned int temp = ra+1;		
	
	if(temp&0xF0000) flags->setFlag(carry,true);
	else flags->setFlag(carry,false);
	
	unsigned short c = (unsigned short) temp;  // Trunca
	
	//verifica Overflow
	flags->setFlag(overflow,false);
	if( (ra & 0x8000) == 0 ) if( (c & 0x8000) != (ra & 0x8000) ) flags->setFlag(overflow, true);

	
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return c;
}

unsigned short ULA::Sub(unsigned short ra, unsigned short rb,Flags *flags)
{
//	unsigned int temp = ra-rb;	
	unsigned int temp = ra + (~rb) + 1;  // complemento de 2	
	
	if(temp&0xF0000) flags->setFlag(carry,true);
	else flags->setFlag(carry,false);
	
	unsigned short c = (unsigned short) temp;  // Trunca
	
	//verifica Overflow
	flags->setFlag(overflow,false);
	if( (ra & 0x8000) != (rb & 0x8000) ) if( (c & 0x8000) != (ra & 0x8000) ) flags->setFlag(overflow, true);

	
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	return c;
}

unsigned short ULA::SubDec(unsigned short ra, unsigned short rb,Flags *flags)
{
//	unsigned int temp = ra-rb-1;		
	unsigned int temp = ra + (~rb) + 1 + 0xFFFF; // complemento de 2
	
	if(temp&0xF0000) flags->setFlag(carry,true);
	else flags->setFlag(carry,false);
	
	unsigned short c = (unsigned short) temp;  // Trunca
	
	//verifica Overflow
	flags->setFlag(overflow,false);
	if( (ra & 0x8000) != (rb & 0x8000) ) if( (c & 0x8000) != (ra & 0x8000) ) flags->setFlag(overflow, true);

	
	if(c!=0){
		flags->setFlag(zero,false);
		flags->setFlag(vero, true);
	}else{
		flags->setFlag(zero,true);
		flags->setFlag(vero,false);
	}
	if(c & 0x8000) flags->setFlag(neg,true);
	else flags->setFlag(neg,false);	
	if(flags->getFlag(zero) || flags->getFlag(neg) ) flags->setFlag(negzero, true);
	else flags->setFlag(negzero, false);
	
	return c;
}
		
